using System;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using Clandestine.Base;

namespace Clandestine.Graphics
{
	// ----- Part 1A. What This Is All About ------
	//		 [aka. "What the fuck is this shit."]
	//
	// Basically, we're keeping it simple. Textures are immutable.
	// We want to hide as much as our implementation about textures as possible from the rest of graphics.
	// We do this by only allowing the code to add/remove textures through the Textures container class.
	// We'll let the code in Graphics reference a -real- Texture object, 'cos we're nice like that.
	// Was going to have a fake Texture struct that got passed around but I realised it's pointless as the
	// render code will need access to a real Texture object.

	/// <summary>
	/// Handles our textures. This is the only class the outside world can use with regards to textures.
	/// [And by outside world, I mean only stuff in .Graphics!]
	/// </summary>
	public class Textures // Todo: implement appropriate System.Collections
	{
		/// <summary>
		/// Our internal method of storing/sorting our textures for use.
		/// </summary>
		private Dictionary<string, Texture> textures = new Dictionary<string, Texture>();
		private Graphics graphics;
		
		/// <summary>
		/// The number of textures this collector currently contains.
		/// </summary>
		public int Count
		{
			get { return textures.Count; }
		}
		
		public Textures(Graphics graphics)
		{
			this.graphics = graphics;
		}

		public Texture AddBitmap(string identifier, Bitmap bitmap, bool treatAsIndexed = false, bool linearInterpolation = false, bool keepAlive = false)
		{
			string filename = identifier;  // if bitmap is null, and we need to load from the filesystem, we need the cased version of identifier!
			identifier = identifier.ToLower();  // Textures is case-insensitive!
			
			lock (textures)
				if (textures.ContainsKey(identifier))  // Do we already have this texture in memory?
				{
					// Ok, then increase the ref. count by one and return the already-loaded texture.
					textures[identifier].ReferenceCount++;
					return textures[identifier];
				}
				else
				{
					// No?  Then it's time to create a new texture!  (A new Texture has a ref-count of 1, incidentally.)
					
					if (bitmap == null) // You should not do this.
						throw new NullReferenceException();
					
					Texture tex = (Texture) new BitmapTexture(graphics, identifier, bitmap, treatAsIndexed, linearInterpolation, keepAlive);
					textures.Add(identifier, tex);
					//Log.i("Created new texture with identifier '{0}'; size {1}x{2}", identifier, bitmap.Size.Width, bitmap.Size.Height);
					return tex;
				}
		}
		
		public Texture AddSub(string parentTexture, Rectangle rect, string identifierSub, bool keepAlive = false)
		{
			parentTexture = parentTexture.ToLower();

			lock (textures)
				if (this.textures.ContainsKey(parentTexture))
					return AddSub(this.textures[parentTexture], rect, identifierSub, keepAlive);
				else
					throw new ArgumentException("Parent texture does not exist, so cannot create subtexture.");
		}
		public Texture AddSub(Texture parentTexture, Rectangle rect, string identifier, bool keepAlive = false)
		{
			identifier = identifier.ToLower();
			
			lock (textures)
				if (textures.ContainsKey(identifier))
					throw new NotImplementedException();
				else
				{
					SubTexture tex = new SubTexture(parentTexture, rect, identifier, keepAlive);
					textures.Add(identifier, (Texture)tex);
					return (Texture)tex;
				}
		}
		
		// Do not use this, please. It is for Graphics.RenderToTexture!
		internal Texture AddBlank(string identifier, Size size, bool linearInterplation = false, bool keepAlive = false)
		{
			identifier = identifier.ToLower();
			
			lock (textures)
				if (textures.ContainsKey(identifier))
				{
					// no need to update ref count, we assume that it's just rendered to again!
					return textures[identifier];
				}
				else
				{
					FramebufferTexture tex = new FramebufferTexture(graphics, identifier, size, linearInterplation, keepAlive);
					textures.Add(identifier, (Texture)tex);
					return (Texture)tex;
				}
		}
		
		public bool Exists(string identifier)
		{
			lock (textures)
				return this.textures.ContainsKey(identifier.ToLower());
		}
		
		public Texture Get(string identifier)
		{
			identifier = identifier.ToLower();

			lock (textures)
				if (textures.ContainsKey(identifier))
				{
					textures[identifier].ReferenceCount++;
					return textures[identifier];
				}
				else
					return null;
		}
		
		public void Remove(Texture texture)
		{
			this.Remove(texture.Identifier);
		}
		
		public void Remove(string identifier)
		{
			identifier = identifier.ToLower();
			lock (textures)
				// if not in our collection, silently do nothing - caller is just being careful, is all!
				if (textures.ContainsKey(identifier))
				{
					Texture t = textures[identifier];
					t.ReferenceCount--;
					t.ReferenceCount = ((t.ReferenceCount <= 0) && (t.KeepAlive)) ? 1 : t.ReferenceCount;
					if (t.ReferenceCount <= 0) // play it safe...
					{	
						textures.Remove(identifier);
						//Log.i("Destroying texture '{0}'", identifier);
						t.Destroy();
					}
				}
		}
	}
}

